package my.websocket.download;

import io.netty.buffer.ByteBuf;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class FileFlushTask implements Runnable {

    private final FileChannel fileChannel;
    private final RandomAccessFile logFile;
    private final AtomicBoolean canceled = new AtomicBoolean(false);

    private final long startTime;
    private long downloadedBytes;

    public FileFlushTask(String fileName, long fileLength, long beginIdx) throws IOException {
        // "r" 以只读方式来打开指定文件夹。如果试图对该RandomAccessFile执行写入方法，都将抛出IOException异常。
        // "rw" 以读写方式打开指定文件。如果该文件尚不存在，则试图创建该文件。
        // "rws" 以读写方式打开指定文件。相对于"rw"模式，还要求对文件内容或元数据的每个更新都同步写入到底层设备。
        // "rwd" 以读写方式打开指定文件。相对于"rw"模式，还要求对文件内容每个更新都同步写入到底层设备。
        File file = new File(fileName);
        RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
        if (!file.exists()) {
            accessFile.setLength(fileLength + beginIdx);
        }
        accessFile.seek(beginIdx);
        this.downloadedBytes = beginIdx;
        this.fileChannel = accessFile.getChannel();
        this.logFile = new RandomAccessFile(fileName + ".log", "rw");
        this.startTime = System.currentTimeMillis();
    }

    @Override
    public void run() {
        while (!canceled.get()) {
            try {
                flush();
                long time = System.currentTimeMillis() - startTime;
                if (time > 1000) {
                    System.out.println("下载速度:" + (downloadedBytes / 1024) / (time / 1000) + "KB/s");
                }
                logFile.seek(0);
                logFile.writeBytes(String.valueOf(downloadedBytes));
                TimeUnit.SECONDS.sleep(2);
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            logFile.close();
            fileChannel.close();
        } catch (IOException e) {
            // ignored
        }
    }

    public void write(ByteBuf buff) throws IOException {
        int bytes = buff.readableBytes();
        int count = 0;
        while (count < bytes) {
            count += fileChannel.write(buff.nioBuffer());
        }
        downloadedBytes += bytes;
    }

    public void flush() throws IOException {
        fileChannel.force(false);
    }

    public void cancel() {
        canceled.set(true);
    }
}
